/* vxbTiK3Am38xxI2c.c - driver for AM38xx I2C module */

/*
 * Copyright (c) 2019 Wind River Systems, Inc.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1) Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * 2) Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * 3) Neither the name of Wind River Systems nor the names of its contributors
 * may be used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/*
modification history
--------------------
19feb19,whe  created (VXWPG-114)
*/

/*
DESCRIPTION
This module implements a driver for the I2C controller presented on TI AM 38XX
processors. The controller is capable of acting as a master or a slave, and each
I2C module instance has one completely independent channel. Access to the I2C
controller is required for certain devices such as EEPROMs and RTCs.

This driver only supports 7 bits addressing, 10 bits addressing is not
supported.

To add the driver to the vxWorks image, add the following component to the
kernel configuration.

\cs
vxprj component add DRV_AM38XX_I2C
\ce

Each I2c device should be bound to a device tree node which requires
below properties:

\cs
compatible:     Specify the programming model for the device.
                It should be set to "ti,am38xx-i2c" and is used
                by vxbus GEN2 for device driver selection.

reg:            Specify the address of the device's resources within
                the address space defined by its parent bus.

def-bus-frequency: Optional, specify the default working clock frequency of the
                I2C BUS in HZ. If not specified, 400000 is used.

interrupts:     Optional, specify interrupt vector of the interrupts that are
                generated by this device, if not specified, poll mode is used.

interrupt-parent: Optional, this property is available to define an interrupt
                parent. if it is missing from a device, it's interrupt parent
                is assumed to be its device tree parent.

pinmux-0:       Optional, specifies the PinMux phandle for the controller.

clocks:         List of clock source phandle and clock parameter pairs,
                one pair present for one clock it uses.

clock-names:    Optional, list of clock source names corresponding to the clock
                source phandles. If more than one clocks listed in the "clocks"
                property, the function clock must be correctly set using "fck"
                as the name to index, or else, the driver may use the wrong
                clock and will fail for the I2C BUS transaction.
\ce

Below is an example which also demonstrats the usage of an I2C EEPROM:

\cs
        i2c@44e0b000
            {
            #address-cells = <1>;
            #size-cells = <0>;
            compatible = "ti,am38xx-i2c";
            clocks = <&clk 67>,<&clk 68>;
            clock-names = "ick","fck";
            def-bus-frequency = <400000>;
            reg = <0x44e0b000 0x1000>;
            pinmux-0 = <&i2c0_pads>;

            eeprom@50
                {
                compatible = "at24, at24c256";
                reg = <0x50>;
                data-scl-frequency = <400000>;
                };
            };
\ce

INCLUDE FILES: vxBus.h vxbI2cLib.h string.h vxbFdtLib.h
*/

/* includes */

#include <vxWorks.h>
#include <vsbConfig.h>
#include <stdlib.h>
#include <string.h>
#include <semLib.h>
#include <taskLib.h>
#include <stdio.h>
#include <ioLib.h>
#include <hwif/vxBus.h>
#include <subsys/int/vxbIntLib.h>
#include <subsys/timer/vxbTimerLib.h>
#include <subsys/pinmux/vxbPinMuxLib.h>
#include <subsys/clk/vxbClkLib.h>
#include <hwif/buslib/vxbFdtI2cLib.h>

#include "vxbTiK3Am38xxI2c.h"

/* defines */

#ifdef  I2C_DBG_ON

#define I2C_DBG_IRQ         0x00000001
#define I2C_DBG_RW          0x00000002
#define I2C_DBG_ERR         0x00000004
#define I2C_DBG_ALL         0xffffffff
#define I2C_DBG_OFF         0x00000000

LOCAL UINT32 i2cDbgMask = I2C_DBG_OFF;
IMPORT FUNCPTR _func_logMsg;

#define I2C_DBG(mask, ...)                              \
    if ((i2cDbgMask & mask) || (mask == I2C_DBG_ALL))   \
        if (_func_logMsg != NULL)                       \
            (* _func_logMsg) (__VA_ARGS__)
#else
#define I2C_DBG(...)
#endif  /* I2C_DBG_ON */

/* forward declarations */

LOCAL STATUS am38xxI2cProbe
    (
    struct vxbDev * pDev
    );

LOCAL STATUS am38xxI2cAttach
    (
    VXB_DEV_ID   pDev
    );

LOCAL VXB_RESOURCE * am38xxI2cResAlloc
    (
    VXB_DEV_ID pDev,
    VXB_DEV_ID pChild,
    UINT32 id
    );

LOCAL STATUS am38xxI2cResFree
    (
    VXB_DEV_ID pDev,
    VXB_DEV_ID pChild,
    VXB_RESOURCE * pRes
    );

LOCAL VXB_RESOURCE_LIST * am38xxI2cResListGet
    (
    VXB_DEV_ID pDev,
    VXB_DEV_ID pChild
    );

LOCAL STATUS am38xxI2cTransfer
    (
    VXB_DEV_ID pDev,
    I2C_MSG *msgs,
    int num
    );

LOCAL VXB_FDT_DEV * am38xxI2cDevGet
    (
    VXB_DEV_ID pDev,
    VXB_DEV_ID pChild
    );

LOCAL STATUS am38xxI2cInit
    (
    VXB_DEV_ID   pDev
    );

LOCAL STATUS am38xxI2cWaitStatus
    (
    VXB_DEV_ID      pDev,
    UINT32          staMsk
    );

LOCAL STATUS am38xxI2cRead
    (
    VXB_DEV_ID      pDev,
    UINT8           devAddr,
    UINT8 *         pDataBuf,
    UINT32          length,
    BOOL            last
    );

LOCAL STATUS am38xxI2cWrite
    (
    VXB_DEV_ID      pDev,
    UINT8           devAddr,
    UINT8 *         pDataBuf,
    UINT32          length,
    BOOL            first,
    BOOL            last
    );

LOCAL void amMcp79410RtcPinMux
    (
    struct vxbDev * pDev
    );

LOCAL VXB_DRV_METHOD am38xxI2cDeviceMethods[] =
    {
        { VXB_DEVMETHOD_CALL (vxbDevProbe),      (FUNCPTR)am38xxI2cProbe},
        { VXB_DEVMETHOD_CALL (vxbDevAttach),     (FUNCPTR)am38xxI2cAttach},
        { VXB_DEVMETHOD_CALL (vxbResourceAlloc), (FUNCPTR)am38xxI2cResAlloc},
        { VXB_DEVMETHOD_CALL (vxbResourceFree),  (FUNCPTR)am38xxI2cResFree},
        { VXB_DEVMETHOD_CALL (vxbResourceListGet),(FUNCPTR)am38xxI2cResListGet},
        { VXB_DEVMETHOD_CALL (vxbI2cXfer),        (FUNCPTR)am38xxI2cTransfer},
        { VXB_DEVMETHOD_CALL (vxbFdtDevGet),      (FUNCPTR)am38xxI2cDevGet},
        VXB_DEVMETHOD_END
    };

VXB_DRV vxbOfTiK3Am38xxI2cCtlrDrv =
    {
    { NULL } ,
    "am38xxI2c",                        /* Name */
    "TI AM38XX I2C controller",         /* Description */
    VXB_BUSID_FDT,                      /* Class */
    0,                                  /* Flags */
    0,                                  /* Reference count */
    am38xxI2cDeviceMethods              /* Method table */
    };

LOCAL const VXB_FDT_DEV_MATCH_ENTRY am38xxI2cMatch[] =
    {
        {
        "ti,am38xx-i2c",                   /* compatible */
        (void *)NULL
        },
        {}                              /* Empty terminated list */
    };

VXB_DRV_DEF (vxbOfTiK3Am38xxI2cCtlrDrv)

/*******************************************************************************
*
* am38xxI2cDevGet - get the FDT child device information
*
* This routine gets the FDT child device information
*
* RETURNS: the device information pointer or NULL if failed to get.
*
* ERRNO: N/A
*/

LOCAL VXB_FDT_DEV * am38xxI2cDevGet
    (
    VXB_DEV_ID pDev,
    VXB_DEV_ID pChild
    )
    {
    I2C_DEV_INFO * pAm3I2cDevInfo;

    if (pChild == NULL)
        return NULL;

    pAm3I2cDevInfo = (I2C_DEV_INFO *)vxbDevIvarsGet(pChild);

    if (pAm3I2cDevInfo == NULL)
        return NULL;

    return &pAm3I2cDevInfo->vxbFdtDev;
    }

/*****************************************************************************
*
* am38xxI2cInit - initialize the I2C module
*
* This routine initializes the I2C module
*
* RETURNS: OK, or ERROR if failed to init.
*
* ERRNO: N/A
*
*/

LOCAL STATUS am38xxI2cInit
    (
    VXB_DEV_ID   pDev
    )
    {
    UINT32 timeout = 0;
    UINT32 val = 0;
    UINT32 psc, scll, sclh;
    I2C_DRV_CTRL * pDrvCtrl;

    if (pDev == NULL)
        {
        return ERROR;
        }
    pDrvCtrl = (I2C_DRV_CTRL*)vxbDevSoftcGet (pDev);
    if (pDrvCtrl == NULL)
        {
        return ERROR;
        }

    /* reset I2C module */

    vxbWrite32 (AM38XX_I2C_HANDLE(pDev),
        (UINT32 *)((char *)AM38XX_I2C_BAR(pDev) + I2C_SYSC), I2C_SYSC_SRST);

    CSR_WRITE_4 (pDev, I2C_CON, 0);

    /* set I2C 12 module clock */

    psc = (pDrvCtrl->fClkFreq / pDrvCtrl->iClkFreq) - 1;

    /* set I2C bus speed */

    scll = (pDrvCtrl->iClkFreq / (pDrvCtrl->busSpeed * 2)) - 7;
    sclh = (pDrvCtrl->iClkFreq / (pDrvCtrl->busSpeed * 2)) - 5;

    CSR_WRITE_4 (pDev, I2C_PSC, psc);
    CSR_WRITE_4 (pDev, I2C_SCLL, scll);
    CSR_WRITE_4 (pDev, I2C_SCLH, sclh);

    /* enable I2C module */

    CSR_WRITE_4 (pDev, I2C_CON, I2C_CON_EN);

    if (pDrvCtrl->rwMode == I2C_RW_MODE_INT)
        {
        CSR_WRITE_4 (pDev, I2C_IRQENABLE_CLR, I2C_IRQSTATUS_ALL);
        CSR_WRITE_4 (pDev, I2C_IRQENABLE_SET, I2C_IRQSTATUS_DEFAULT);

        val = CSR_READ_4(pDev, I2C_BUFSTAT);
        val = (val & I2C_BUFSTAT_FIFODEPTH_MASK) >> I2C_BUFSTAT_FIFODEPTH_SHIFT;

        switch (val)
            {
            case I2C_BUFSTAT_FIFODEPTH_8:
                pDrvCtrl->fifoLen = 8;
                break;
            case I2C_BUFSTAT_FIFODEPTH_16:
                pDrvCtrl->fifoLen = 16;
                break;
            case I2C_BUFSTAT_FIFODEPTH_32:
                pDrvCtrl->fifoLen = 32;
                break;
            case I2C_BUFSTAT_FIFODEPTH_64:
                pDrvCtrl->fifoLen = 64;
                break;
            default:
                pDrvCtrl->fifoLen = 8;
                break;
            }
        pDrvCtrl->rxThreshold = pDrvCtrl->fifoLen / 2;
        pDrvCtrl->txThreshold = pDrvCtrl->fifoLen / 2;;
        CSR_WRITE_4 (pDev, I2C_BUF, ((pDrvCtrl->rxThreshold - 1 ) <<
                     I2C_BUF_RXTRSH_SHIFT) |
                     (pDrvCtrl->txThreshold - 1));
        }
    else
        {

        /* enable all IRQ bits in polling mode  */

        CSR_WRITE_4 (pDev, I2C_IRQENABLE_SET, I2C_IRQSTATUS_ALL);
        }

    /* clear status register */

    CSR_WRITE_4 (pDev, I2C_IRQSTATUS, I2C_IRQSTATUS_ALL);

    /* clear counter register */

    CSR_WRITE_4 (pDev, I2C_CNT, 0x0);

    /* wait for reset done */
    do  {
        vxbUsDelay(1);
        } while (!(CSR_READ_4 (pDev, I2C_SYSS) & I2C_SYSS_RDONE) &&
                  (timeout++ < I2C_TIMEOUT));

    if (timeout > I2C_TIMEOUT)
        {
        return ERROR;
        }

    pDrvCtrl->i2cStatus = OK;
    return OK;
    }

/******************************************************************************
*
* am38xxI2cProbe - probe for device presence at specific address
*
* This routine checks for the I2C contoller (or compatible) device at the
* base address. We assume one is present at that address, but we need to verify.
*
* RETURNS: OK if probe passes and assumed a valid I2C controller
* (or compatible) device. ERROR otherwise.
*
* ERRNO: N/A
*/

LOCAL STATUS am38xxI2cProbe
    (
    VXB_DEV_ID pDev
    )
    {
    return vxbFdtDevMatch (pDev, am38xxI2cMatch, NULL);
    }

/*******************************************************************************
*
* am38xxI2cIsr - interrupt service routine
*
* This routine handles interrupts of I2C.
*
* RETURNS: N/A
*
* ERRNO: N/A
*/

LOCAL void am38xxI2cIsr
    (
    VXB_DEV_ID      pDev
    )
    {
    UINT16 status;
    I2C_DRV_CTRL * pDrvCtrl;
    UINT32 bytesToRx = 0;
    UINT32 bytesToTx = 0;

    if (pDev == NULL)
        {
        return;
        }

    pDrvCtrl = (I2C_DRV_CTRL*)vxbDevSoftcGet(pDev);
    if (pDrvCtrl == NULL)
        {
        return;
        }

    status = (UINT16)CSR_READ_4 (pDev, I2C_IRQSTATUS);
    status &= (UINT16)CSR_READ_4 (pDev, I2C_IRQENABLE_CLR);

    I2C_DBG (I2C_DBG_RW,
             "am38xxI2cIsr:I2C_IRQSTATUS 0x%04x\n", status, 0, 0, 0, 0, 0);

    if (status & I2C_IRQSTATUS_AL)
        {
        I2C_DBG (I2C_DBG_ERR,
                "am38xxI2cIsr:I2C ARB lost 0x%x\n", status, 0, 0, 0, 0, 0);

        pDrvCtrl->i2cStatus = ERROR;
        CSR_WRITE_4 (pDev, I2C_IRQSTATUS, I2C_IRQSTATUS_AL);
        (void) semGive (pDrvCtrl->i2cDataSem);
        return;
        }

    if (status & I2C_IRQSTATUS_NACK)
        {
        I2C_DBG (I2C_DBG_ERR,
                 "am38xxI2cIsr:I2C NACK 0x%x\n", status, 0, 0, 0, 0, 0);

        pDrvCtrl->i2cStatus = ERROR;
        CSR_WRITE_4 (pDev, I2C_IRQSTATUS, I2C_IRQSTATUS_NACK);
        (void) semGive (pDrvCtrl->i2cDataSem);
        return;
        }

    if (status & I2C_IRQSTATUS_ROVR)
        {
        I2C_DBG (I2C_DBG_ERR,
                 "am38xxI2cIsr:I2C RX overrun 0x%x\n", status, 0, 0, 0, 0, 0);

        pDrvCtrl->i2cStatus = ERROR;
        (void) semGive (pDrvCtrl->i2cDataSem);
        return;
        }

    if (status & I2C_IRQSTATUS_XUDF)
        {
        I2C_DBG (I2C_DBG_ERR,
                 "am38xxI2cIsr:I2C TX underflow 0x%x\n", status, 0, 0, 0, 0, 0);

        pDrvCtrl->i2cStatus = ERROR;
        (void) semGive (pDrvCtrl->i2cDataSem);
        return;
        }

    if (status & I2C_IRQSTATUS_AERR)
        {
        I2C_DBG (I2C_DBG_ERR,
                 "am38xxI2cIsr:I2C AERR 0x%x\n", status, 0, 0, 0, 0, 0);

        pDrvCtrl->i2cStatus = ERROR;
        CSR_WRITE_4 (pDev, I2C_IRQSTATUS, I2C_IRQSTATUS_AERR);
        (void) semGive (pDrvCtrl->i2cDataSem);
        return;
        }

    if (status & I2C_IRQSTATUS_XDR)
        {

        /* TX: remain bytes to be transfered is less than txThreshold */

        bytesToTx = (CSR_READ_4 (pDev, I2C_BUFSTAT) &
                    I2C_BUFSTAT_TXSTAT_MASK) >> I2C_BUFSTAT_TXSTAT_SHIFT;

        I2C_DBG (I2C_DBG_IRQ,
                 "I2C_IRQ XDR remain 0x%x bytes\n",
                 pDrvCtrl->dataLength, 0, 0, 0, 0, 0);

        for (; (bytesToTx) && (pDrvCtrl->dataLength) ;)
            {
            CSR_WRITE_4(pDev, I2C_DATA, *(pDrvCtrl->dataBuf));
            pDrvCtrl->dataLength--;
            pDrvCtrl->dataBuf++;
            bytesToTx--;
            }

        if(pDrvCtrl->dataLength == 0)
            {

            /* restore the buffer head pointer */

            pDrvCtrl->dataBuf = NULL;

            if (semGive (pDrvCtrl->i2cDataSem) == ERROR)
                {
                I2C_DBG (I2C_DBG_ERR,
                         "am38xxI2cIsr:semGive error\n",
                         0, 0, 0, 0, 0, 0);
                }
            }

        CSR_WRITE_4 (pDev, I2C_IRQSTATUS, I2C_IRQSTATUS_XDR);
        return;
        }

    if (status & I2C_IRQSTATUS_XRDY)
        {

        /* TX: remain bytes to be transfered is greater than txThreshold */

        bytesToTx = pDrvCtrl->txThreshold;

        I2C_DBG (I2C_DBG_IRQ,
                 "I2C_IRQ XRDY remain 0x%x bytes\n", bytesToTx, 0, 0, 0, 0, 0);

        for (; (bytesToTx) && (pDrvCtrl->dataLength) ;)
            {
            CSR_WRITE_4(pDev, I2C_DATA, *(pDrvCtrl->dataBuf));
            pDrvCtrl->dataLength--;
            pDrvCtrl->dataBuf++;
            bytesToTx--;
            }

        if(pDrvCtrl->dataLength == 0)
            {
            /* restore the buffer head pointer */

            pDrvCtrl->dataBuf = NULL;

            if (semGive (pDrvCtrl->i2cDataSem) == ERROR)
                {
                I2C_DBG (I2C_DBG_ERR,
                         "am38xxI2cIsr:semGive error\n",
                         0, 0, 0, 0, 0, 0);
                }
            }

        CSR_WRITE_4 (pDev, I2C_IRQSTATUS, I2C_IRQSTATUS_XRDY);
        return;
        }

    if (status & I2C_IRQSTATUS_RDR)
        {

        /*
         * STOP received and RX FIFO level is below the configured threshold
         * (RXTRSH).
         * RX buffer status indicates the number of bytes to be transferred
         * from the FIFO.
         */

        bytesToRx = CSR_READ_4(pDev, I2C_BUFSTAT);
        bytesToRx = (bytesToRx & I2C_BUFSTAT_RXSTAT_MASK) >>
                         I2C_BUFSTAT_RXSTAT_SHIFT;

        I2C_DBG (I2C_DBG_IRQ,
                 "I2C_IRQ RDR remain 0x%x - 0x%x bytes\n",
                 bytesToRx, pDrvCtrl->dataLength, 0, 0, 0, 0);

        /* read data from FIFO */

        for (; (bytesToRx) && (pDrvCtrl->dataLength); )
            {
            *(pDrvCtrl->dataBuf) = CSR_READ_4(pDev, I2C_DATA) & 0xff;
            pDrvCtrl->dataLength--;
            pDrvCtrl->dataBuf++;
            bytesToRx--;
            }

        if(pDrvCtrl->dataLength == 0)
            {
            pDrvCtrl->dataBuf = NULL;

            if (semGive (pDrvCtrl->i2cDataSem) == ERROR)
                {
                I2C_DBG (I2C_DBG_ERR,
                        "am38xxI2cIsr:semGive error\n", 0, 0, 0, 0, 0, 0);
                }
            }

        CSR_WRITE_4 (pDev, I2C_IRQSTATUS, I2C_IRQSTATUS_RDR);
        return;
        }

    if (status & I2C_IRQSTATUS_RRDY)
        {

        /* RX FIFO level is above the configured threshold (RXTRSH) */

        bytesToRx = pDrvCtrl->rxThreshold;

        I2C_DBG (I2C_DBG_IRQ,
                 "I2C_IRQ RRDY remain 0x%x bytes\n", bytesToRx, 0, 0, 0, 0, 0);

        for (; (bytesToRx) && (pDrvCtrl->dataLength); )
            {
            *(pDrvCtrl->dataBuf) = CSR_READ_4(pDev, I2C_DATA) & 0xff;
            pDrvCtrl->dataLength--;
            pDrvCtrl->dataBuf++;
            bytesToRx--;
            }

        if(pDrvCtrl->dataLength == 0)
            {
            pDrvCtrl->dataBuf = NULL;

            if (semGive (pDrvCtrl->i2cDataSem) == ERROR)
                {
                I2C_DBG (I2C_DBG_ERR,
                        "am38xxI2cIsr:semGive error\n", 0, 0, 0, 0, 0, 0);
                }
            }

        CSR_WRITE_4 (pDev, I2C_IRQSTATUS, I2C_IRQSTATUS_RRDY);
        }
    return;
    }

/*****************************************************************************
*
* am38xxI2cProbeChild - probe child device
*
* This function probes child devices.
*
* RETURNS: N/A
*
* ERRNO: N/A
*/

LOCAL void am38xxI2cProbeChild
    (
    VXB_DEV_ID pDev,
    VXB_FDT_DEV * pParFdtDev
    )
    {
    int offset = pParFdtDev->offset;
    VXB_DEV_ID pCur = NULL;
    I2C_DEV_INFO *pI2cDevInfo;
    VXB_FDT_DEV * pFdtDev;

    for (offset = VX_FDT_CHILD(offset); offset > 0;
         offset = VX_FDT_PEER(offset))
        {
        pCur = NULL;

        if (vxbDevCreate (&pCur) != OK)
            continue;

        pI2cDevInfo = (I2C_DEV_INFO *)vxbMemAlloc(sizeof(*pI2cDevInfo));

        if (pI2cDevInfo == NULL)
            {
            (void) vxbDevDestroy(pCur);
            continue;
            }

        pFdtDev = &pI2cDevInfo->vxbFdtDev;

        vxbFdtDevSetup(offset, pFdtDev);
        vxbDevNameSet(pCur, pFdtDev->name, FALSE);

        /* Get the device register and interrupt infomation  */

        if (vxbResourceInit(&pI2cDevInfo->vxbResList) != OK)
            {
            (void) vxbDevDestroy (pCur);
            vxbMemFree (pI2cDevInfo);
            continue;
            }

        if(vxbFdtRegGet(&pI2cDevInfo->vxbResList, pFdtDev) == ERROR)
            {
            vxbFdtResFree(&pI2cDevInfo->vxbResList);
            vxbMemFree(pI2cDevInfo);
            (void) vxbDevDestroy(pCur);
            continue;
            }

        if (vxbFdtIntGet(&pI2cDevInfo->vxbResList, pFdtDev) == ERROR)
            {
            vxbFdtResFree(&pI2cDevInfo->vxbResList);
            vxbMemFree(pI2cDevInfo);
            (void) vxbDevDestroy(pCur);
            continue;
            }

        /* Assign the bus internal variable and type  */

        vxbDevIvarsSet(pCur, (void *) pI2cDevInfo);
        vxbDevClassSet(pCur, VXB_BUSID_FDT);

        (void) vxbDevAdd(pDev, pCur);
        }
    }

/*****************************************************************************
*
* am38xxI2cAttach - VxBus attcach method
*
* This function implements the VxBus attach method for a I2C controller
* device instance.
*
* RETURNS: OK or ERROR if failed to attach.
*
* ERRNO: N/A
*/

LOCAL STATUS am38xxI2cAttach
    (
    VXB_DEV_ID         pDev
    )
    {
    I2C_DRV_CTRL     * pDrvCtrl = NULL;
    VXB_CLK          * pClk = NULL;
    VXB_RESOURCE_ADR * pResAdr = NULL;
    VXB_RESOURCE     * pRes = NULL;
    VXB_FDT_DEV      * pFdtDev;
    void             * pValue;
    int                len;
    int                slen;
    BOOL               useClkNames = FALSE;

    if (pDev == NULL)
        {
        return ERROR;
        }

    pFdtDev = vxbFdtDevGet (pDev);
    if (pFdtDev == NULL)
        {
        return ERROR;
        }

    /* create controller driver context structure for core */

    pDrvCtrl = (I2C_DRV_CTRL *) vxbMemAlloc (sizeof (I2C_DRV_CTRL));
    if (pDrvCtrl == NULL)
        {
        return ERROR;
        }

    /* save pDrvCtrl in VXB_DEVICE structure */

    vxbDevSoftcSet (pDev, pDrvCtrl);

    /* save instance ID */

    pDrvCtrl->i2cDev   = pDev;
    pDrvCtrl->iClkFreq = AM38XX_I2C_ICLK_FREQ;

    pRes = vxbResourceAlloc (pDev, VXB_RES_IRQ, 0);
    if (pRes == NULL)
        {
        pDrvCtrl->rwMode = I2C_RW_MODE_POLL;
        pDrvCtrl->intRes = NULL;
        }
    else
        {
        pDrvCtrl->rwMode = I2C_RW_MODE_INT;
        pDrvCtrl->intRes = pRes;
        pRes = NULL;
        }

    if ((pValue = (void *)vxFdtPropGet (pFdtDev->offset,
                                        "clock-names", &len)) != NULL)
        {
        /* more than one clock? */

        slen = (int)strlen((char *)pValue);
        if (len > (slen + 1))
            {
            useClkNames = TRUE;
            }
        }

    if (useClkNames)
        {
        /* get the fck if more than one clocks */

        pClk = vxbClkGet (pDev, AM38XX_I2C_FCK_NAME);
        }
    else
        {
        /* get the first clock */

        pClk = vxbClkGet (pDev, NULL);
        }

    if (pClk == NULL)
        {
        I2C_DBG ("am38xxI2cAttach failed, cannot find the function clock\n",
                 1, 2, 3, 4, 5, 6);
        goto errOut;
        }

    pRes = vxbResourceAlloc(pDev, VXB_RES_MEMORY, 0);
    if (pRes == NULL)
        {
        I2C_DBG ("am38xxI2cAttach failed to alloc memory resource\n",
                 1, 2, 3, 4, 5, 6);
        goto errOut;
        }

    pResAdr = (VXB_RESOURCE_ADR *)pRes->pRes;
    if (pResAdr == NULL)
        {
        I2C_DBG ("am38xxI2cAttach failed to alloc memory address resource\n",
                 1, 2, 3, 4, 5, 6);
        goto errOut;
        }

    pDrvCtrl->i2cHandle = pResAdr->pHandle;
    pDrvCtrl->bar       = (ULONG) pResAdr->virtual;

    if ((pValue = (void*)vxFdtPropGet (pFdtDev->offset,
                                       I2C_BUS_SPEED_CFG_NAME, NULL)) != NULL)
        {
        pDrvCtrl->busSpeed = vxFdt32ToCpu (*(UINT32 *) pValue);
        }
    else
        {
        pDrvCtrl->busSpeed = FAST_MODE;
        }
    pDrvCtrl->defBusSpeed = pDrvCtrl->busSpeed;

    /* now enable clocks and pads before access the registers */

    (void) vxbPinMuxEnable (pDev);
    (void) vxbClkEnableAll (pDev);

    pDrvCtrl->fClkFreq = (UINT32)vxbClkRateGet (pClk);
    if (pDrvCtrl->fClkFreq == CLOCK_RATE_INVALID)
        pDrvCtrl->fClkFreq = AM38XX_I2C_FCLK_FREQ;

    /* init RTC pinmux */

    pRes = vxbResourceAlloc (pDev, VXB_RES_MEMORY, 1);  
    if (pRes != NULL)
        { 
        pResAdr = (VXB_RESOURCE_ADR *)pRes->pRes;
        if (pResAdr == NULL)
            {
            goto errOut;
            }

        pDrvCtrl->pinMuxRegHandle = pResAdr->pHandle;
        pDrvCtrl->pinMuxRegBase = (void *)pResAdr->virtual;\

        amMcp79410RtcPinMux (pDev); 
        }

    /* reset I2C module */

    if (am38xxI2cInit (pDev) == ERROR)
        goto errOut;

    pDrvCtrl->dataBuf = NULL;
    pDrvCtrl->i2cDevSem = semMCreate (SEM_Q_PRIORITY | SEM_DELETE_SAFE |
                                      SEM_INVERSION_SAFE);
    if (pDrvCtrl->i2cDevSem == SEM_ID_NULL)
        {
        I2C_DBG (I2C_DBG_ERR,
                 "am38xxI2cAttach: i2cDevSem create failed\n",
                 0, 0, 0, 0, 0, 0);
        goto errOut;
        }

    pDrvCtrl->i2cDataSem = semBCreate (SEM_Q_PRIORITY, SEM_EMPTY);
    if (pDrvCtrl->i2cDataSem == SEM_ID_NULL)
        {
        I2C_DBG (I2C_DBG_ERR,
                 "am38xxI2cAttach: i2cDataSem Ceate failed\n",
                 0, 0, 0, 0, 0, 0);
        goto errOut;
        }

    if (pDrvCtrl->rwMode == I2C_RW_MODE_INT)
        {
        CSR_WRITE_4 (pDev, I2C_IRQENABLE_CLR, 0xffff);
        CSR_WRITE_4 (pDev, I2C_IRQENABLE_SET,
                     (I2C_IRQENABLE_AL | I2C_IRQENABLE_NACK |
                      I2C_IRQENABLE_RRDY | I2C_IRQENABLE_XRDY));

        /* connect and enable interrupt */

        if (vxbIntConnect (pDev, pDrvCtrl->intRes, (VOIDFUNCPTR) am38xxI2cIsr,
                           pDev) == ERROR)
            {
            pDrvCtrl->rwMode = I2C_RW_MODE_POLL;
            }
        else
            {
            if (vxbIntEnable (pDev, pDrvCtrl->intRes) == ERROR)
                {
                pDrvCtrl->rwMode = I2C_RW_MODE_POLL;
                (void) vxbIntDisconnect (pDev, pDrvCtrl->intRes);
                }
            }
        }

    am38xxI2cProbeChild (pDev, pFdtDev);

    return OK;

errOut:
    if (pDrvCtrl->i2cDataSem != NULL)
        (void)semDelete (pDrvCtrl->i2cDataSem);
    if (pDrvCtrl->i2cDevSem != NULL)
        (void)semDelete (pDrvCtrl->i2cDevSem);
    if (pRes != NULL)
        (void)vxbResourceFree (pDev, pRes);
    if (pDrvCtrl->intRes != NULL)
        (void)vxbResourceFree (pDev, pDrvCtrl->intRes);

    vxbMemFree (pDrvCtrl);
    vxbDevSoftcSet (pDev, NULL);

    return ERROR;
    }

/*******************************************************************************
*
* am38xxI2cResAlloc - allocate resource for child device
*
* This routine allocates resource for child device and go corresponding operation
* based on its type.
*
* RETURNS: allocated resource pointer, or NULL if failure.
*
* ERRNO: N/A
*/

LOCAL VXB_RESOURCE * am38xxI2cResAlloc
    (
    VXB_DEV_ID pDev,
    VXB_DEV_ID pChild,
    UINT32 id
    )
    {
    I2C_DEV_INFO *pAm3I2cDevInfo;
    VXB_RESOURCE * vxbRes;
    VXB_RESOURCE_ADR * vxbAdrRes;

    pAm3I2cDevInfo = (I2C_DEV_INFO *)vxbDevIvarsGet(pChild);

    vxbRes = vxbResourceFind(&pAm3I2cDevInfo->vxbResList, id);

    if (vxbRes == NULL)
        return NULL;

    if ((VXB_RES_TYPE(vxbRes->id) == VXB_RES_MEMORY) ||
         (VXB_RES_TYPE(vxbRes->id) == VXB_RES_IO))
        {
        vxbAdrRes = vxbRes->pRes;
        vxbAdrRes->virtual = (VIRT_ADDR)(vxbAdrRes->start);
        return vxbRes;
        }
    else if ((VXB_RES_TYPE(vxbRes->id) == VXB_RES_IRQ) &&
             (vxbIntMap (vxbRes) == OK))
        return vxbRes;
    else
        {
        return NULL;
        }
    }

/*******************************************************************************
*
* am38xxI2cResFree - free the resource for child device
*
* This routine frees the resource allocated by calling am38xxI2cResAlloc.
*
* RETURNS: OK if free successfully, otherwise ERROR.
*
* ERRNO: N/A
*/

LOCAL STATUS am38xxI2cResFree
    (
    VXB_DEV_ID pDev,
    VXB_DEV_ID pChild,
    VXB_RESOURCE * pRes
    )
    {
    if ((pChild == NULL) || (pRes == NULL))
        return ERROR;

    return OK;
    }

/*******************************************************************************
*
* am38xxI2cResListGet - get the resource list of specific device
*
* This routine gets the resource list of specific device.
*
* RETURNS: resource list pointer, or NULL if failed to get.
*
* ERRNO: N/A
*/

LOCAL VXB_RESOURCE_LIST * am38xxI2cResListGet
    (
    VXB_DEV_ID pDev,
    VXB_DEV_ID pChild
    )
    {
    I2C_DEV_INFO * pI2cDevInfo;

    pI2cDevInfo = (I2C_DEV_INFO *)vxbDevIvarsGet (pChild);

    if (pI2cDevInfo == NULL)
        return NULL;

    return  &pI2cDevInfo->vxbResList;
    }

/*******************************************************************************
*
* am38xxI2cWaitBusFree - wait for I2C bus free
*
* This routine waits for I2C bus free
*
* RETURNS: OK, or ERROR if timeout
*/

LOCAL STATUS am38xxI2cWaitBusFree
    (
    VXB_DEV_ID      pDev
    )
    {
    UINT32 status, timeout = 0;

    VXB_ASSERT (pDev != NULL, ERROR)

    do  {
        status = CSR_READ_4 (pDev, I2C_IRQSTATUS);
        vxbUsDelay(1);
        }while ((status & I2C_IRQSTATUS_BB) && (timeout++ < I2C_TIMEOUT));

    if (timeout > I2C_TIMEOUT)
        {
        I2C_DBG (I2C_DBG_ERR,
            "am38xxI2cWaitBusFree:timeout status 0x%x\n", status, 0, 0, 0, 0, 0);

        return ERROR;
        }

    return OK;
    }

/*****************************************************************************
*
* am38xxI2cRead - read a sequence of bytes from a device
*
* This function reads <length> bytes from the I2C device and stores them
* in the buffer specified by <pDataBuf>. The read begins at an offset
* specified previously in the transaction. If too many bytes are requested,
* the read will return ERROR, though any successfully read bytes will be
* returned in <pDataBuf>.
*
* RETURNS: OK if read completed successfully, otherwise ERROR
*
* ERRNO: N/A
*/

LOCAL STATUS am38xxI2cRead
    (
    VXB_DEV_ID      pDev,
    UINT8           devAddr,
    UINT8 *         pDataBuf,
    UINT32          length,
    BOOL            last
    )
    {
    UINT32          i;
    UINT64          timeout = 0;
    I2C_DRV_CTRL *  pDrvCtrl;
    UINT32          conflags = I2C_CON_EN | I2C_CON_MST | I2C_CON_STT;

    if (pDev == NULL)
        {
        return ERROR;
        }
    pDrvCtrl = (I2C_DRV_CTRL*)vxbDevSoftcGet (pDev);
    if (pDrvCtrl == NULL)
        {
        return ERROR;
        }

    if (length == 0)
        {
        return OK;
        }

    if (length > I2C_CNT_MAX)
        {
        I2C_DBG (I2C_DBG_ERR, "am38xxI2cRead:length error\n", 0, 0, 0, 0, 0, 0);
        return ERROR;
        }

    if ((pDrvCtrl->intEnabled) && (pDrvCtrl->rwMode == I2C_RW_MODE_INT))
        {
        pDrvCtrl->dataBuf = pDataBuf;
        pDrvCtrl->dataLength = length;

        /* enable RRDY and RDR */

        CSR_WRITE_4 (pDev, I2C_IRQENABLE_CLR, I2C_IRQSTATUS_ALL);

        CSR_WRITE_4 (pDev, I2C_IRQENABLE_SET,
            I2C_IRQSTATUS_DEFAULT | I2C_IRQSTATUS_RDR | I2C_IRQSTATUS_RRDY);
        }

    if (last)
        {
        conflags |= I2C_CON_STP;
        }

    CSR_WRITE_4 (pDev, I2C_CNT, length);

    /* clear FIFO */

    CSR_WRITE_4 (pDev, I2C_BUF, CSR_READ_4 (pDev, I2C_BUF) |
        I2C_BUF_RXFIFO_CLR | I2C_BUF_TXFIFO_CLR);

    CSR_WRITE_4 (pDev, I2C_SA,  devAddr);
    CSR_WRITE_4 (pDev, I2C_CON, conflags);

    if ((pDrvCtrl->intEnabled) && (pDrvCtrl->rwMode == I2C_RW_MODE_INT))
        {
        /* calculate timeout value */

        timeout = (UINT64) SEM_TIMEOUT * (UINT32) vxbSysClkRateGet () * 8 * length;
        if (pDrvCtrl->busSpeed == 0)
            {
            I2C_DBG (I2C_DBG_ERR, "am38xxI2cRead:busSpeed is 0\n",
                     0, 0, 0, 0, 0, 0);
            goto error;
            }

        timeout /= pDrvCtrl->busSpeed;
        timeout = SEM_TIMEOUT > timeout ? SEM_TIMEOUT : timeout;

        if (semTake (pDrvCtrl->i2cDataSem, (UINT32) timeout) == ERROR)
            {
            I2C_DBG (I2C_DBG_ERR, "am38xxI2cRead:semTake error timeout %d\n",
                     (UINT32) timeout, 0, 0, 0, 0, 0);
            goto error;
            }

        /* disable RRDY and XRDY */

        CSR_WRITE_4 (pDev, I2C_IRQENABLE_CLR, I2C_IRQSTATUS_ALL);
        CSR_WRITE_4 (pDev, I2C_IRQENABLE_SET, I2C_IRQSTATUS_DEFAULT);

        if (pDrvCtrl->i2cStatus == ERROR)
            goto error;
        }
    else
        {
        for ( i = 0; i < length; i++ )
            {
            if (am38xxI2cWaitStatus (pDev,  I2C_IRQSTATUS_RRDY) == ERROR)
                {
                I2C_DBG (I2C_DBG_ERR,
                    "am38xxI2cRead:am38xxI2cWaitStatus error\n",
                    0, 0, 0, 0, 0, 0);

                goto error;
                }

            pDataBuf[i] = CSR_READ_4 (pDev, I2C_DATA) & 0xff;
            CSR_WRITE_4 (pDev, I2C_IRQSTATUS, I2C_IRQSTATUS_RRDY);
            }
        }

    /* poll operation completion */

    timeout = 0;
    do  {
        vxbUsDelay(1);
        }while (!(CSR_READ_4 (pDev, I2C_IRQSTATUS_RAW) & I2C_IRQSTATUS_ARDY) &&
                (timeout++ < I2C_TIMEOUT));
    CSR_WRITE_4 (pDev, I2C_IRQSTATUS, I2C_IRQSTATUS_ARDY);

    if (timeout > I2C_TIMEOUT)
        {
        I2C_DBG (I2C_DBG_ERR,
            "am38xxI2cRead:wait ARDY timeout\n", 0, 0, 0, 0, 0, 0);

        goto error;
        }

    return OK;

error:

    I2C_DBG (I2C_DBG_ERR, "am38xxI2cRead failed \n", 0, 0, 0, 0, 0, 0);

    if (am38xxI2cInit (pDev) == ERROR)
        {
        I2C_DBG (I2C_DBG_ERR,
            "am38xxI2cRead:am38xxI2cInit error\n", 0, 0, 0, 0, 0, 0);
        }

    return ERROR;
    }

/*****************************************************************************
*
* am38xxI2cWrite - write a sequence of bytes to a device
*
* This function writes <length> bytes from the I2C device read from the
* buffer specified by <pDataBuf>. The write begins at an offset specified
* previously in the transaction. If too many bytes are requested, the write
* will return ERROR.
*
* RETURNS: OK if write completed successfully, otherwise ERROR
*
* ERRNO: N/A
*/

LOCAL STATUS am38xxI2cWrite
    (
    VXB_DEV_ID      pDev,
    UINT8           devAddr,
    UINT8 *         pDataBuf,
    UINT32          length,
    BOOL            first,
    BOOL            last
    )
    {
    UINT32          i;
    UINT64          timeout = 0;
    I2C_DRV_CTRL *  pDrvCtrl;
    UINT32          conflags = I2C_CON_EN | I2C_CON_MST | I2C_CON_TRX |
                               I2C_CON_STT;
    if (pDev == NULL)
        {
        return ERROR;
        }
    pDrvCtrl = (I2C_DRV_CTRL*)vxbDevSoftcGet (pDev);
    if (pDrvCtrl == NULL)
        {
        return ERROR;
        }

    if (length == 0)
        {
        return OK;
        }

    if (length > I2C_CNT_MAX)
        {
        I2C_DBG (I2C_DBG_ERR, "am38xxI2cWrite:length error\n", 0, 0, 0, 0, 0, 0);
        return ERROR;
        }

    if (first && (am38xxI2cWaitBusFree (pDev) == ERROR))
        {
        I2C_DBG (I2C_DBG_ERR,
            "am38xxI2cWrite:am38xxI2cWaitBusFree error\n", 0, 0, 0, 0, 0, 0);

        goto error;
        }

    if ((pDrvCtrl->intEnabled) && (pDrvCtrl->rwMode == I2C_RW_MODE_INT))
        {
        pDrvCtrl->dataBuf = pDataBuf;
        pDrvCtrl->dataLength = length;

        /* enable XRDY and XDR */

        CSR_WRITE_4 (pDev, I2C_IRQENABLE_CLR, I2C_IRQSTATUS_ALL);

        CSR_WRITE_4 (pDev, I2C_IRQENABLE_SET,
            (I2C_IRQSTATUS_DEFAULT | I2C_IRQENABLE_XRDY | I2C_IRQSTATUS_XDR));
        }

    if (last)
        conflags |= I2C_CON_STP;

    CSR_WRITE_4 (pDev, I2C_CNT, length);

    /* clear FIFO */

    CSR_WRITE_4 (pDev, I2C_BUF, CSR_READ_4 (pDev, I2C_BUF) |
        I2C_BUF_RXFIFO_CLR | I2C_BUF_TXFIFO_CLR);

    CSR_WRITE_4 (pDev, I2C_SA, devAddr);
    CSR_WRITE_4 (pDev, I2C_CON, conflags);

    if ((pDrvCtrl->intEnabled) && (pDrvCtrl->rwMode == I2C_RW_MODE_INT))
        {
        /* calculate timeout value */

        timeout = (UINT64) SEM_TIMEOUT * (UINT32) vxbSysClkRateGet () * 8 * length;
        if (pDrvCtrl->busSpeed == 0)
            {
            I2C_DBG (I2C_DBG_ERR, "am38xxI2cWrite:busSpeed is 0\n",
                     0, 0, 0, 0, 0, 0);
            goto error;
            }

        timeout /= pDrvCtrl->busSpeed;
        timeout = SEM_TIMEOUT > timeout ? SEM_TIMEOUT : timeout;

        if (semTake (pDrvCtrl->i2cDataSem, (UINT32) timeout) == ERROR)
            {
            I2C_DBG (I2C_DBG_ERR,
                "am38xxI2cWrite:semTake error\n", 0, 0, 0, 0, 0, 0);

            goto error;
            }

        /* disable XRDY and XDR */

        CSR_WRITE_4 (pDev, I2C_IRQENABLE_CLR,
            (I2C_IRQENABLE_XRDY | I2C_IRQSTATUS_XDR));

        if (pDrvCtrl->i2cStatus == ERROR)
            goto error;
        }
    else
        {
        for (i = 0; i < length; i++)
            {
            if (am38xxI2cWaitStatus (pDev, I2C_IRQSTATUS_XRDY) == ERROR)
                {
                I2C_DBG (I2C_DBG_ERR,
                    "am38xxI2cWrite:am38xxI2cWaitStatus error\n",
                    0, 0, 0, 0, 0, 0);

                goto error;
                }

            CSR_WRITE_4 (pDev, I2C_DATA, pDataBuf[i]);
            CSR_WRITE_4 (pDev, I2C_IRQSTATUS, I2C_IRQSTATUS_XRDY);
            }
        }

    /* poll operation completion */

    timeout = 0;
    do  {
        vxbUsDelay(1);
        }while (!(CSR_READ_4 (pDev, I2C_IRQSTATUS_RAW) & I2C_IRQSTATUS_ARDY) &&
                (timeout++ < I2C_TIMEOUT));
    CSR_WRITE_4 (pDev, I2C_IRQSTATUS, I2C_IRQSTATUS_ARDY);

    if (timeout > I2C_TIMEOUT)
        {
        I2C_DBG (I2C_DBG_ERR,
            "am38xxI2cWrite:wait ARDY timeout\n", 0, 0, 0, 0, 0, 0);

        goto error;
        }

    return OK;

error:

    I2C_DBG (I2C_DBG_ERR, "am38xxI2cWrite failed \n", 0, 0, 0, 0, 0, 0);

    if (am38xxI2cInit (pDev) == ERROR)
        {
        I2C_DBG (I2C_DBG_ERR,
            "am38xxI2cWrite:am38xxI2cInit error\n", 0, 0, 0, 0, 0, 0);
        }

    return ERROR;
    }

/*******************************************************************************
*
* am38xxI2cTransfer - VxBus I2C device generic write support routine
*
* This function receive msg from upper layer and perform I2C transfer.
*
* RETURNS: OK / ERROR
*
* ERRNO: N/A
*/

LOCAL STATUS am38xxI2cTransfer
    (
    VXB_DEV_ID      pDev,
    I2C_MSG *       msgs,
    int             num
    )
    {
    I2C_MSG *       pmsg;
    int             i;
    I2C_DRV_CTRL *  pDrvCtrl;

    if (pDev == NULL || msgs == NULL || num <= 0)
        return ERROR;

    pDrvCtrl = (I2C_DRV_CTRL *) vxbDevSoftcGet (pDev);

    if (pDrvCtrl == NULL)
        return ERROR;

    pDrvCtrl->intEnabled = sysIntEnableFlagGet ();

    if (pDrvCtrl->intEnabled)
        {
        if (semTake (pDrvCtrl->i2cDevSem, WAIT_FOREVER) == ERROR)
            {
            I2C_DBG (I2C_DBG_ERR,
                "am38xxI2cTransfer:semTake error\n", 0, 0, 0, 0, 0, 0);

            return ERROR;
            }
        }

    /* if the scl is 0, use default bus speed */

    if (msgs[0].scl == 0)
        {
        msgs[0].scl = pDrvCtrl->defBusSpeed;
        }

    if (msgs[0].scl != pDrvCtrl->busSpeed)
        {
        pDrvCtrl->busSpeed = msgs->scl;
        (void) am38xxI2cInit (pDev);
        }

    for (i = 0; i < num; i++)
        {
        pmsg = &msgs[i];

        I2C_DBG (I2C_DBG_RW, "Doing %s %d bytes to 0x%02x - %d of %d messages\n",
            pmsg->flags & I2C_M_RD ? "read" : "write",
            pmsg->len, pmsg->addr, i + 1, num, 6);

        if (pmsg->flags & I2C_M_RD)
            {
            /* pmsg->addr is slave addr */
            if (am38xxI2cRead (pDev, (UINT8) pmsg->addr, pmsg->buf, pmsg->len,
                               i == (num - 1)) != OK)
                goto xfailed;
            }
        else
            {
            if (am38xxI2cWrite (pDev, (UINT8) pmsg->addr, pmsg->buf, pmsg->len,
                                i == 0, i == (num - 1)) != OK)
                goto xfailed;
            }
        }

    vxbUsDelay ((int) pmsg->wrTime);

    if (pDrvCtrl->intEnabled)
        {
        (void) semGive (pDrvCtrl->i2cDevSem);
        }

    return OK;

xfailed:

    I2C_DBG (I2C_DBG_ERR, "transfer failed \n", 0, 0, 0, 0, 0, 0);

    if (pDrvCtrl->intEnabled)
        {
        (void) semGive (pDrvCtrl->i2cDevSem);
        }

    return ERROR;
}

/*******************************************************************************
*
* am38xxI2cWaitStatus - wait for I2C controller to the specify status
*
* This routine waits for I2C controller to the specify status
*
* RETURNS: OK, or ERROR if timeout
*/

LOCAL STATUS am38xxI2cWaitStatus
    (
    VXB_DEV_ID      pDev,
    UINT32          staMsk
    )
    {
    UINT32 status, timeout = 0;

    if (pDev == NULL)
        {
        return ERROR;
        }

    do  {
        status = CSR_READ_4 (pDev, I2C_IRQSTATUS);
        vxbUsDelay(1);
        }while (!(status & staMsk) && (timeout++ < I2C_TIMEOUT));

    if (timeout > I2C_TIMEOUT)
        {
        I2C_DBG (I2C_DBG_ERR,
            "am38xxI2cWaitStatus:timeout\n", 0, 0, 0, 0, 0, 0);

        return ERROR;
        }

    if (status & I2C_IRQSTATUS_NACK)
        {
        I2C_DBG (I2C_DBG_ERR,
            "am38xxI2cWaitStatus:no acknowledgement\n", 0, 0, 0, 0, 0, 0);

        return ERROR;
        }

    if (status & I2C_IRQSTATUS_AL)
        {
        I2C_DBG (I2C_DBG_ERR,
            "am38xxI2cWaitStatus:arbitration lost\n", 0, 0, 0, 0, 0, 0);

        return ERROR;
        }

    return OK;
    }

/*******************************************************************************
*
* amMcp79410RtcPinMux - init RTC PinMux
*
* This routine handles PinMux of RTC.
*
* RETURNS: N/A
*
* ERRNO: N/A
*/

LOCAL void amMcp79410RtcPinMux
    (
    struct vxbDev * pDev
    )
    { 
    CSR_WRITE_PINMUX_4(pDev, I2C0_SCL_PINMUX_REG_OFFSET, 
                       AM65X_IOPAD_VAL (I2C_PIN_INPUT_PULLUP, 
                                        I2C_PIN_MUX_MODE));  /* SPI0_CLK */
    CSR_WRITE_PINMUX_4(pDev, I2C0_SDA_PINMUX_REG_OFFSET, 
                       AM65X_IOPAD_VAL (I2C_PIN_INPUT_PULLUP, 
                                        I2C_PIN_MUX_MODE));  /* SPI0_D0 */
    }
